import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:lib_database/lib_database.dart';
import 'package:lib_util/log/log.dart';
import 'package:lib_util/utils/utils.dart';
import 'package:decimal/decimal.dart';
import 'package:lib_web3/lib_solana_web3.dart';
import 'package:lib_web3/lib_web3_method.dart';

import '../network/rpc_http.dart';

class TokenListNotifier with ChangeNotifier {
  bool isLoading = false;

  WalletInfo? _walletInfo;

  final List<Token> _tokenList = [];

  String walletInfoValueUsd = '\$ 0.00';
  Token? _nativeToken;
  final List<Token> _tokenFungibleList = []; //已经添加到钱包的tokens  资产页面 代币列表
  final List<Token> _tokenNonFungibleList = [];
  final List<Token> _customTokenList = []; // 添加代币 列表 用到的

  StreamSubscription? _subscription;

  //代币搜索相关
  String get searchText => _searchText;
  String _searchText = ''; //搜索内容
  List<Token> _searchCustomTokenList = []; //搜索结果
  List<Token> get searchCustomTokenList => _searchCustomTokenList;

  TokenListNotifier();

  int get walletInfoId => null == _walletInfo ? 0 : _walletInfo!.id;

  List<Token> get tokenList => _tokenList;

  Token? get nativeToken => _nativeToken;

  List<Token> get tokenFungibleList => _tokenFungibleList;

  List<Token> get tokenNonFungibleList => _tokenNonFungibleList;

  List<Token> get customTokenList => _customTokenList;

  void setTokenList(WalletInfo info) {
    //清除 token列表
    if (null != _subscription) {
      _subscription!
          .cancel()
          .then((value) => Config.logger.d('cancel subscription'));
    }

    _walletInfo = info;
    // Config.logger.d('set token list: $info');

    _refreshTokenList();
  }

  void _refreshTokenList() {
    _tokenList.clear();
    _nativeToken = null;
    _tokenFungibleList.clear();
    _tokenNonFungibleList.clear();
    _customTokenList.clear();
    List<Token> query =
        ObjectBox().tokenModel.getTokenListByWalletInfoId(_walletInfo!.id);
    if (query.isNotEmpty) {
      _tokenList.addAll(query);
    }
    _complete(_walletInfo!);
  }

  void updateTokenList(WalletInfo info) {
    setTokenList(info);
    notifyListeners();

    // refreshTokenListFromServer();
  }

  _complete(WalletInfo walletInfo) {
    for (var t in _tokenList) {
      if (t.contractType == TokenType.token20.name) {
        if (t.isAdded) {
          _tokenFungibleList.add(t);
        } else {
          _customTokenList.add(t);
        }
      } else if (t.contractType == TokenType.token721.name ||
          t.contractType == TokenType.token1155.name) {
        _tokenNonFungibleList.add(t);
      } else if (t.contractType == TokenType.native.name) {
        _nativeToken = t;
      }
    }
    _nativeToken ??= Token(
        walletInfoId: walletInfo.id,
        contractType: TokenType.native.name,
        contract: '',
        name: '',
        symbol: walletInfo.getChain()!.symbol,
        decimals: 18,
        enable: true,
        isAdded: true);

    if (nativeToken!.id == 0) {
      nativeToken!.id = ObjectBox().tokenModel.putTokenList([nativeToken!])[0];
    } else {
      if (walletInfo.getChain() != null) {
        String chainSymbol = walletInfo.getChain()!.symbol;
        if (nativeToken!.symbol != chainSymbol) {
          nativeToken!.symbol = chainSymbol;
          ObjectBox().tokenModel.updateToken(nativeToken!);
        }
      }
    }

    // Config.logger
    //     .d('provider:$nativeToken,chain:${walletInfo.getChain()!.symbol}');

    // Decimal totalValue = Decimal.zero;
    // tokenFungibleList.map((t) {
    //   if (!Utils.isEmpty(t.valueusd)) {
    //     try {
    //       totalValue += Decimal.parse(t.valueusd!);
    //     } catch (e) {
    //       Config.logger.e(e);
    //     }
    //   }
    // }).toString();
    // Config.logger
    //     .d('provider:$nativeToken,chain:${walletInfo.getChain()!.symbol}');
    // try {
    //   if (!Utils.isEmpty(_nativeToken!.valueusd)) {
    //     totalValue += Decimal.parse(_nativeToken!.valueusd!);
    //   }
    //
    //   walletInfoValueUsd = '\$${Utils.formatDecimalBalance(totalValue)}';
    // } catch (e) {
    //   Config.logger.e(e);
    // }
  }

  void refreshTokenListFromLocal() {
    _refreshTokenList();
    notifyListeners();
  }

  Future refreshTokenListFromServer() async {
    if (null == _walletInfo) {
      throw 'wallet not init';
    }
    Completer completer = Completer();
    if(isLoading==false){
      isLoading=true;
    }
    // 仅仅 获取 主币 信息。
    _subscription = RPCHttp().requestTokenList(_walletInfo!).asStream().listen(
        (data) async {
      Chain nc = _nativeToken!.getChain()!;
      if (_nativeToken!.symbol != data['tokenSymbol'] &&
          _nativeToken!.symbol != data['chainShortName'] &&
          nc.shortName != data['chainShortName']) {
        completer.completeError('symbol not equal');
        return;
      }
      String totalPriceUsd='0';
      _nativeToken!.priceusd = data['tokenPriceUsd'] == null ||
              double.parse(data['tokenPriceUsd']) == 0
          ? (_nativeToken!.priceusd ?? '')
          : data['tokenPriceUsd'];
      _nativeToken!.change24h = data['tokenChange24h'] ?? '';
      _nativeToken!.isAdded = true;
      dynamic ha = data['holdingAmount'] ?? _nativeToken!.balance;
      String? holdingAmount = ha is int ? ha.toString() : ha;
      _nativeToken!.balance =
          Utils.isEmpty(holdingAmount) ? '0.00' : holdingAmount;
      int walletInfoId = _walletInfo!.id;
      //    修改为 已查询 已添加 在列表中的 列表
      List<Token> allCachedTokenList =
          ObjectBox().tokenModel.getIsAddedToken20ByWalletInfoId(walletInfoId);
      // 将 添加的 代币 拿去 查 余额 以及 Usdt 的价格
      print('allCachedTokenList  ${allCachedTokenList}');
      List<Future<Token>> futures = [];
      List<String> symbols = ['${_nativeToken?.symbol.toUpperCase()}USDT'];
      for (var i = 0; i < allCachedTokenList.length; i++) {
        Token tokenItem = allCachedTokenList[i];
        if (tokenItem.contractType == TokenType.native.name) {
          continue;
        }
        print('${tokenItem.symbol.toUpperCase()}USDT');
        symbols.add('${tokenItem.symbol.toUpperCase()}USDT');
        // tokenItem.symbol
        futures.add(RPCHttp().requestTokenItemList(_walletInfo!, tokenItem));
      }
      List<Token> cacheList = await Future.wait(futures);
      try{
        Map<String,dynamic> tokenUData= await RPCHttp().getTokenListIncreaseU(symbols);
        Iterable isSuccessList= await Future.wait(cacheList.map((e) {
          try{
            if(e.symbol.toUpperCase() == 'USDT'){
              e.change24h = '0.00';
              e.priceusd = '1';
            }else{
              e.priceusd = tokenUData['${e.symbol.toUpperCase()}USDT']['priceU'];
              e.change24h= tokenUData['${e.symbol.toUpperCase()}USDT']['change'];
            }
            e.valueusd = Utils.formatDecimalBalance(
                Decimal.parse(e!.priceusd!) *
                    Decimal.parse(e!.balance!),
                fractionDigits: Config.balanceFixedNum);
           totalPriceUsd= Utils.formatDecimalBalance(Decimal.parse(totalPriceUsd) + Decimal.parse(e.valueusd ?? '0'),
               fractionDigits: Config.marketFixedNum);
            return Future(() => ObjectBox().tokenModel.updateToken(e));
          }catch(e){
            return Future(() => null);
          }
        }));
        if(tokenUData['${_nativeToken?.symbol.toUpperCase()}USDT']!=null&&tokenUData['${_nativeToken?.symbol.toUpperCase()}USDT'].length>0){
          _nativeToken!.priceusd = tokenUData['${_nativeToken?.symbol.toUpperCase()}USDT']['priceU'];
          _nativeToken!.change24h= tokenUData['${_nativeToken?.symbol.toUpperCase()}USDT']['change'];
        }
      }catch(e){
        print('更新数据 异常 $e');
      }
      try {
        if (_nativeToken!.priceusd!.isNotEmpty &&
            _nativeToken!.balance!.isNotEmpty) {
          _nativeToken!.valueusd = Utils.formatDecimalBalance(
              Decimal.parse(_nativeToken!.priceusd!) *
                  Decimal.parse(_nativeToken!.balance!),
              fractionDigits: Config.balanceFixedNum);
        }
        totalPriceUsd= Utils.formatDecimalBalance(Decimal.parse(totalPriceUsd) + Decimal.parse(_nativeToken!.valueusd ?? '0'),
            fractionDigits: Config.marketFixedNum);
        print('_nativeToken  ${_nativeToken.toString()}');
        if(_walletInfo!=null){
          _walletInfo!.balance = totalPriceUsd;
          await ObjectBox().walletInfoModel.updateWalletBalance(_walletInfo!);
        }
        await ObjectBox().tokenModel.updateToken(_nativeToken!);
      } catch (e) {
        Log().e(e.toString());
      }
      _refreshTokenList();
      notifyListeners();
      completer.complete();

      isLoading=false;
    }, onError: (error) {
      isLoading=false;
      completer.completeError(error);
      Log.logger.e(error);
    });
    return completer.future.onError((error, stackTrace) async {
      // await refreshLocalTokenListFromServer();
      _refreshTokenList();
      notifyListeners();
    }).timeout(const Duration(seconds: 10000), onTimeout: () {
      if (null != _subscription) {
        _subscription!.cancel();
      }
      throw 'request time out';
    });
  }

  Future refreshLocalTokenListFromServer({Token? token20}) async {
    if (!_walletInfo!.isCustomChain() &&
        !_walletInfo!.getChain()!.isBtcOrBtcTestnet()) {
      List<Token> token20List = null != token20
          ? [token20]
          : ObjectBox().tokenModel.getToken20ListByWalletInfoId(walletInfoId);
      Log().d(token20List);
      await RPCHttp()
          .refreshTokenListFast(_walletInfo!, token20List, _nativeToken!);
    }
  }

  Token _composeToken(
      int walletInfoId, TokenType type, Map<String, dynamic> t) {
    String symbol = t['token'];
    String contract = t['tokenContractAddress'];
    Token token = ObjectBox().tokenModel.getTokenByWalletInfoIdAndTokenContract(
            _walletInfo!.id, contract) ??
        Token(
            walletInfoId: walletInfoId,
            contractType: type.name,
            contract: contract,
            name: symbol,
            symbol: symbol);

    token.balance = t['holdingAmount'].toString();
    token.priceusd = t['priceUsd'];
    token.valueusd = t['valueUsd'];
    token.change24h = t['change24h'];

    Utils.formatTokenBalancePriceValueFromServer(token, type);

    return token;
  }

  //增加token
  Future addCustomTokens(Token token) async {
    await ObjectBox().tokenModel.addToken(token);
    _tokenFungibleList.add(token);
    notifyListeners();
  }

  //更新token
  void updateTokens(Token token) async {
    await ObjectBox().tokenModel.updateToken(token);
    _refreshTokenList();
    notifyListeners();
  }

  Future updateTokenBalance(Token? token) async {
    try {
      if (token == null) return token;
      Chain? chain = token.getChain();
      String rpc =
          ObjectBox().nodeInfoModel.getSelectedNodeInfo(chain!.shortName).url;
      WalletInfo? _walletInfo =
          ObjectBox().walletInfoModel.getWalletInfoById(token.walletInfoId);
      String balance = token.balance ?? '0';
      // 类型是错的
      if (chain.type == ChainType.eth) {
        if (token.contractType == 'native') {
          //主币
          balance = await FlutterWeb3
              .getMainTokenBalance(rpc, _walletInfo!.address);
        } else if (token.contractType == 'token20') {
          //代币
          balance = await FlutterWeb3
              .getTokenBalance( rpc, token.contract,_walletInfo!.address, decimals: token.decimals);
        }
      }
      if(chain.type == ChainType.sol){
        if (token.contractType == 'native') {
          //主币
          balance = await SolanaWeb3
              .getMainTokenBalance(rpc, _walletInfo!.address);
        } else if (token.contractType == 'token20') {
          //代币
          balance = await SolanaWeb3
              .getTokenBalance( rpc, token.contract,_walletInfo!.address, decimals: token.decimals);
        }
      }
      token.balance = balance;
      await ObjectBox().tokenModel.updateToken(token);
      _refreshTokenList();
      notifyListeners();
      Log.logger.e('更新余额:$balance');
      return token;
    } catch (err) {
      return token;
    }
  }

  //添加token
  void addToken(Token token) async {
    await ObjectBox().tokenModel.addToken(token);
    _refreshTokenList();
    notifyListeners();
  }

  //删除token
  void deleteTokens(Token token) async {
    await ObjectBox().tokenModel.deleteToken(token.id);
    _refreshTokenList();
    notifyListeners();
  }

  //拖拽重新排序
  void reorderTokens(int oldIndex, int newIndex) {
    if (oldIndex < newIndex) {
      //因为先删除，所以old小于new的话，删除后要向前一个位置
      newIndex -= 1;
    }

    var token = _tokenFungibleList.removeAt(oldIndex);
    _tokenFungibleList.insert(newIndex, token);

    ObjectBox()
        .tokenModel
        .deleteTokenList(_tokenFungibleList.map((e) => e.id).toList());
    _tokenFungibleList.forEach((element) {
      element.id = 0;
    });
    ObjectBox().tokenModel.addTokenList(_tokenFungibleList);

    notifyListeners();
  }

  //搜索匹配的内容
  void searchListWithText(String text) {
    _searchText = text;

    //这里做搜索
    _searchCustomTokenList =
        _customTokenList.where((element) => _filter(element, text)).toList();

    notifyListeners();
  }

  bool _filter(Token model, String text) {
    if (text.isEmpty) {
      return true;
    }
    RegExp regex = RegExp(".*${text}.*", caseSensitive: false);
    return regex.hasMatch(model.symbol);
  }

  //重新清空搜索词
  void resetSearchText() {
    _searchCustomTokenList = _customTokenList;
    _searchText = "";

    notifyListeners();
  }
}
